/* 算法实现模板 */
import AlgorithmBase from "./AlgorithmBase";
import React from "react";
import ReactDom from 'react-dom';
import { loadDataset, txtToMatrix , mdRender} from "../../../common/Common";
import MatView2D, {createRandomPointStyleDict} from "../../../visual/MatView2D";
import Mat from "../../../common/Mat";
import TMatrix from "../../../common/TMatrix";
import Chart from 'chart.js/auto';
import ChartJs from "../../../visual/ChartJs";
class DualFormPerceptron extends AlgorithmBase{
  constructor(){
    super('DualFormPerceptron');
    this.model          = {w:new Mat.zeros([1, 2]),b:0,eta:1.0}
    this.descriptionUrl = `${this.homepage}/data/slm/ch2/README.md`;
    this.datasetUrl     = `${this.homepage}/data/slm/ch2/dataset.txt`;
    this.count =1
  }
  init(){ 
    this.reset();

    this.initTrainDS();
  }

  reset(){
    this.dataset = undefined;
    this.trainDS = undefined;
    this.model   = {w:new Mat.zeros([1, 2]),b:0,eta:1.0}

    this.clear();

    this.initDescription();
    this.updateModel();
  }
  
  // 模型UI
  initModelUI(){
    this.addLabel(this.domElementList.model, '学习率:');
    this.etaInput = this.addFloatInput(
      this.domElementList.model, this.model.eta, 
      (event)=>{
        const val = parseFloat(event.target.value);
        if(val<0){
          this.model.eta = 0
          event.target.value = 0
        }
        else this.model.eta = val;
        this.updateModel();
      });

    this.modelDiv = this.addDiv(this.domElementList.model);
  }

  // 渲染模型到页面
  updateModel(w, b){
    this.update = false
    if(w === undefined) w = this.model.w;
    if(b === undefined) b = this.model.b;
    const modelString = String.raw
    `$$ \begin{aligned} w&:=[${w}] \\ b&:=${b} \\ \eta&:=${this.model.eta}\end{aligned} $$`
   
    mdRender(`${modelString}`, this.outputDiv);
    // 显示数据集
    if(this.trainDS){
    
      ReactDom.unmountComponentAtNode(this.inputDiv);
      this.inputDiv.style.width = '600px'
      if(this.count!=1){
        console.log('!=1');
        this.update = true

        var x=[0,0]
        var y=[0,0]
        x[0] = -b /(w[0][1]/w[0][0])
        y[1] = -b 
        //this.chart.data.datasets[2].data=[x,y]
        this.line = [x,y];


        //old
        //this.chart.update()

       
        ReactDom.render(
          <React.StrictMode>
          <ChartJs
            canvas = {this.inputDiv}
            datasetnum ={2}
            dataset1 = {this.dataset1}
            dataset2 = {this.dataset2}
            line = {this.line}
          ></ChartJs></React.StrictMode>,
          this.inputDiv
        )
        return ;
      }
      this.count++

    this.line =[[2,0],[0,7]]
    const ctx = document.getElementById('myChart');
    const DATA_COUNT = 7;
    const NUMBER_CFG = {count: DATA_COUNT, min: -100, max: 100};
    var dataset1 =[]
    var dataset2 =[]
    
    
    for(let i =0;i<this.dataset.length;i++){
      if(this.dataset[i][this.dataset[i].length-1]=='1'){
        dataset1.push(this.dataset[i])
      }else{
        dataset2.push(this.dataset[i])
      }
    }
    this.dataset1 = dataset1
    this.dataset2 = dataset2


    ReactDom.render(
      <React.StrictMode>
      <ChartJs
        canvas = {this.inputDiv}
        dataset1 = {this.dataset1}
        dataset2 = {this.dataset2}
        line = {this.line}
        update = {this.update}
      ></ChartJs></React.StrictMode>,
      this.inputDiv
    )
    

 
    } 
    
    
    
    

  }

  // 输入数据UI
  initInputUI(){

    // 文件选择器
    const fileSelector      = document.createElement('input');
    fileSelector.type       = 'file';
    fileSelector.hidden     = true;
    fileSelector.onchange   = this.handleFileChange.bind(this);   
    
    this.addButton(this.domElementList.input, "获取服务器数据", this.initTrainDS.bind(this));
    this.addButton(this.domElementList.input, "使用本地数据", this.openFileSelector.bind(this));
    this.domElementList.input.appendChild(fileSelector);
    this.addButton(this.domElementList.input, "训练", this.train.bind(this));
    this.playButton = this.addButton(this.domElementList.input, "训练动画", this.trainAnimation.bind(this));
    this.playButton.hidden = true;

    this.labelTrainTips = this.addLabel(this.domElementList.input, '');
    this.inputDiv       = this.addDiv(this.domElementList.input);
    this.inputDiv.ref  = 'inputRef'
    //this.canvas         = this.addCanvas(this.domElementList.input);
    this.fileSelector  = fileSelector;
  }

  // 输出UI
  initOutputUI(){
    this.outputDiv   = this.addDiv(this.domElementList.output);
    this.trainLogDiv = this.addDiv(this.domElementList.output);
  }

  // 获取描述md文件
  initDescription(){
    fetch(this.descriptionUrl).then((response)=>{
      return response.text();
    }).then((txt)=>{

      this.reandMD(txt, this.descriptionDiv)
    })
  }

  // 获取服务器数据集
  initTrainDS(){
    this.prepare(this.datasetUrl);
  }

  // 准备数据训练数据
  prepare(url){
    fetch(url).then((response)=>{
      return response.text();
    }).then((txt)=>{
      this.dataset = txtToMatrix(txt);
      this.trainDS = loadDataset(this.dataset);
      this.pointStyleDict = createRandomPointStyleDict(this.trainDS.Labels);
      this.updateModel();
    })
  }

  openFileSelector(){
    this.fileSelector.click();
  }
  // 获取文件路径加载
  handleFileChange(event){
    const files = event.target.files;

    for(let i = 0, il = files.length; i < il; i++){
      let reader = new FileReader();
      reader.onload = (e) => {      
        this.prepare(e.target.result);
        if(i === files.length - 1){
          event.target.value = '';
        }
      };
      reader.readAsDataURL(files[i])
    }
  }

  // 训练
  train(){
    this.playButton.hidden = true;
    const trainDS = this.trainDS;
    if(!trainDS) return;

    let X = new Mat.Mat(trainDS.Mat);//数据集的特征值不包含标签
    let y = new Mat.Mat(trainDS.Labels);//对应的标签
    var w = new Mat.zeros([1, 2]);
    var alist = new Mat.zeros([1, X.length]);//放a值长度为数据个数
    var b = 0.0;
    var eta = parseFloat(this.model.eta);
    var count = 0;
    var isDone = false;
    var trainHistory = [];    // 训练历史
    var trainMDLog   = "## 迭代过程\r\n";    // 训练日志-使用MD编写
    trainMDLog += "|迭代次数|误分类点|$b$|"
    for (let k = 0; k < X.length; k++) {
      trainMDLog +=String.raw`$a_${k+1}$|`
    }
    trainMDLog+="\r\n"
    for (let k = 0; k < 3+X.length; k++) {
      trainMDLog += "|:---:"
    }
    trainMDLog+="|\r\n"
    var gList = new Mat.Mat([3,3])
    for(let i = 0 ;i<X.length;i++){
      for(let j = 0;j<X.length;j++){
        gList[i][j] = Mat.dot(X.slice([i,i+1]),X.slice([j,j+1])) 
      }
    }//获取G矩阵
    //
    var done = false

    while(!done){
      count++
      console.log('第',count,"次执行");
      w = new Mat.zeros([1, 2]);
      var done =true
      for(let i = 0 ;i<X.length;i++){
        var missValue = 0
        for(let j=0 ;j<X.length;j++){
          missValue += alist[0][j] * y[j] * gList[i][j]
        }
        missValue += b
        missValue = missValue * y[i]
        if (missValue <= 0) {
          alist[0][i] += eta
          b += y[i] * eta
          for(let k=0 ;k<X.length;k++){
            w = Mat.add(w,Mat.multiply(X.slice([k,k+1]),alist[0][k]*y[k][0]))
          }

          trainHistory.push({ errPoint:i, w:w, b:b, time:new Date()} );
          let tmp =''
          for(let k = 0 ; k<X.length ;k++){
            tmp +=String.raw`${alist[0][k]}|`
          }
          trainMDLog += String.raw`|${count}|$x_${i+1}$|${b}|${tmp}`+"\r\n";
          done = false
          break;
        }
      }
    }
    this.model.w = w;
    this.model.b = b;
    this.updateModel(trainHistory[trainHistory.length-1].w,trainHistory[trainHistory.length-1].b);
    this.labelTrainTips.innerText = '训练完毕';
    this.playButton.hidden = false;
    this.trainHistory = trainHistory;
    mdRender(trainMDLog, this.trainLogDiv);
  }

  trainAnimation(){
    if(this.trainHistory && !this.animation){
      this.trainHistoryIndex = 0;
      this.animation  = setInterval(this.playHistory.bind(this), 1000);
    }
  }

  playHistory(){
    if(this.trainHistoryIndex < this.trainHistory.length){
      const lastest = this.trainHistory[this.trainHistoryIndex];
      this.updateModel(lastest.w, lastest.b);
      this.labelTrainTips.innerText = `训练时间:${lastest.time.toString()}`;
      this.trainHistoryIndex += 1;
    }else{
      clearInterval(this.animation);
      this.animation = '';
      this.labelTrainTips.innerText = `播放完毕`;
    }
  }
}




export default DualFormPerceptron;